﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace KinConquer.Entities
{
    public class Screen
    {
        private Dictionary<uint, IMapObject> ScreenDictionary;
        private IMapObject[] m_Screen;
        private Hero Hero;

        public Screen(Hero _Client)
        {
            Hero = _Client;
            ScreenDictionary = new Dictionary<uint, IMapObject>(50);
            m_Screen = new IMapObject[0];
        }
        public IMapObject[] Objects
        {
            get
            {
                return m_Screen;
            }
        }
        public bool Contains(UInt32 uid)
        {
            return ScreenDictionary.ContainsKey(uid);
        }
        public bool Add(IMapObject Base)
        {
            lock (ScreenDictionary)
            {
                if (!ScreenDictionary.ContainsKey(Base.Identifier))
                {
                    ScreenDictionary.Add(Base.Identifier, Base);
                    IMapObject[] tmp_Screen = new IMapObject[ScreenDictionary.Count];
                    ScreenDictionary.Values.CopyTo(tmp_Screen, 0);
                    m_Screen = tmp_Screen;
                    return true;
                }
            }
            return false;
        }
        public unsafe void Remove(uint ID)
        {
            lock (ScreenDictionary)
            {
                IMapObject o;
                if (ScreenDictionary.TryGetValue(ID, out o))
                {
                    if (ScreenDictionary.Remove(ID))
                    {
                        IMapObject[] tmp_Screen = new IMapObject[ScreenDictionary.Count];
                        ScreenDictionary.Values.CopyTo(tmp_Screen, 0);
                        m_Screen = tmp_Screen;

                        Packets.Structures.DataPacket removeEntity = Packets.Structures.DataPacket.Create();
                        removeEntity.DataType = Packets.Structures.DataPacket.DataTypes.RemoveEntity;
                        removeEntity.Identifier = o.Identifier;
                        Hero.Owner.Send(&removeEntity);

                        if (o.ObjectType.HasFlag(ObjectType.Hero))
                        {
                            removeEntity.Identifier = Hero.Identifier;
                            (o as Hero).Owner.Send(&removeEntity);
                        }
                    }
                }
            }
        }
        public void Cleanup()
        {
            bool remove;
            foreach (IMapObject Base in m_Screen)
            {
                remove = (Entities.Calculations.Misc.Distance(Hero.X, Hero.Y, Base.X, Base.Y)
                    >= Constants.ViewDistance);
                if (!remove)
                    remove = Base.DMap != Hero.DMap;

                if (remove)
                {
                    if (Base.ObjectType.HasFlag(ObjectType.Hero))
                    {
                        Hero pPlayer = Base as Hero;
                        lock (pPlayer.Screen.ScreenDictionary)
                        {
                            pPlayer.Screen.ScreenDictionary.Remove(Hero.Identifier);
                        }
                    }
                    lock (ScreenDictionary)
                    {
                        Remove(Base.Identifier);
                    }
                }
            }
        }
        public void FullWipe()
        {
            lock (ScreenDictionary)
            {
                ScreenDictionary.Clear();
                m_Screen = new IMapObject[0];
            }
        }
        public IMapObject FindObject(uint UID)
        {
            if (UID == 0)
                return null;
            else if (UID == Hero.Identifier)
                return Hero;
            IMapObject obj;
            lock (ScreenDictionary)
                ScreenDictionary.TryGetValue(UID, out obj);
            return obj;
        }

        public void GetScreen(Boolean spawnBack, Boolean cleanup)
        {
            if (cleanup)
                Hero.Screen.Cleanup();
            int Distance;

            foreach (Hero h in Program.HeroPool.EnumerableValues)
            {
                if (h != null)
                {
                    if (h.Map == Hero.Map &&
                        h.Identifier != Hero.Identifier)
                    {
                        if (h.DMap == Hero.DMap)
                        {
                            if (!Hero.Screen.Contains(h.Identifier))
                            {
                                Distance = Entities.Calculations.Misc.Distance(Hero.X, Hero.Y, h.X, h.Y);
                                if (Distance <= Constants.ViewDistance)
                                {
                                    Hero.Screen.Add(h);
                                    Hero.Owner.Send(h.SpawnPacket);
                                    
                                    if (spawnBack)
                                    {
                                        h.Screen.Add(Hero);
                                        h.Owner.Send(Hero.SpawnPacket);
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }

        public unsafe void Send(Byte[] data)
        {
            fixed (Byte* b = data)
                Send(b);
        }
        public unsafe void Send(void* data)
        {
            IntPtr ptr = new IntPtr(data);
            Byte* b = (Byte*)ptr;
            Send(b);
        }
        public unsafe void Send(Byte* data)
        {
            var heros = from h in m_Screen where h.ObjectType.HasFlag(ObjectType.Hero) select h;
            foreach (IMapObject o in heros)
            {
                (o as Hero).Owner.Send(data);
            }
        }
        
    }
}
